﻿using IOP.Extension.Convert;
using IOP.Protocols.MQTT.Extension;
using System;
using System.Text;

namespace IOP.Protocols.MQTT.Package
{
    /// <summary>
    /// 发布包
    /// </summary>
    public struct PublishPackage : IMQTTPackage
    {
        /// <summary>
        /// 报文类型
        /// </summary>
        public ControlPacketType PacketType { get => ControlPacketType.PUBLISH; }

        private uint _RemainingLength;
        /// <summary>
        /// 剩余长度
        /// </summary>
        public uint RemainingLength 
        {
            get
            {
                if (QoS != QoSType.QoS0) return _RemainingLength + 4;
                else return _RemainingLength + 2;
            }
        }
        /// <summary>
        /// DUP
        /// </summary>
        public bool DUP;
        /// <summary>
        /// QoS
        /// </summary>
        public QoSType QoS;
        /// <summary>
        /// RETAIN
        /// </summary>
        public bool RETAIN;

        private string _TopicName;
        /// <summary>
        /// 主题名
        /// </summary>
        public string TopicName
        {
            get 
            {
                if (_TopicName == null) _TopicName = string.Empty;
                return _TopicName;
            }
            set
            {
                if (_TopicName != value)
                {
                    if (_TopicName != null) _RemainingLength -= (uint)(_TopicName.Length);
                    if (value != null)
                    {
                        if (Encoding.UTF8.GetByteCount(value) > ushort.MaxValue) throw new MQTTPackageException("ClientIdentifier string lenght is out of 65535 bytes");
                        _RemainingLength += (uint)value.Length;
                        _TopicName = value;
                    }
                    else _TopicName = string.Empty;
                }
            }
        }

        /// <summary>
        /// 报文标识符
        /// </summary>
        public ushort PacketIdentifier;

        private byte[] _Body;
        /// <summary>
        /// 数据
        /// </summary>
        public byte[] Body
        {
            get
            {
                if (_Body == null) _Body = new byte[0];
                return _Body;
            }
            set
            {
                if(_Body != value)
                {
                    if(_Body != null) _RemainingLength -= (uint)_Body.Length;
                    if (value != null)
                    {
                        _RemainingLength += (uint)value.Length;
                        _Body = value;
                    }
                    else _Body = new byte[0];
                }
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="body"></param>
        /// <param name="dup"></param>
        /// <param name="packetId"></param>
        /// <param name="qoS"></param>
        /// <param name="retain"></param>
        /// <param name="topicName"></param>
        public PublishPackage(string topicName, ushort packetId, byte[] body, QoSType qoS = QoSType.QoS0, bool dup = false, bool retain = false)
        {
            _TopicName = string.Empty;
            _RemainingLength = 0;
            _Body = new byte[0];
            PacketIdentifier = 0;

            DUP = dup;
            QoS = qoS;
            RETAIN = retain;
            TopicName = topicName;
            PacketIdentifier = packetId;
            Body = body;
        }

        /// <summary>
        /// 转换为字节码
        /// </summary>
        /// <returns></returns>
        public Span<byte> ToBytes()
        {
            int remain = RemainingLength.GetRemainBytesLength();
            var length = RemainingLength + remain + 1;

            Span<byte> result = new byte[length];
            int index = 0;

            var type = (byte)((byte)PacketType << 4);
            var dup = (byte)((DUP ? 1 : 0) << 3);
            var retaim = (byte)(RETAIN ? 1 : 0);
            var qoS = (byte)((byte)QoS << 1);
            var first = (byte)(type | dup | retaim | qoS);
            result[index++] = first;
            RemainingLength.WriteRemainLengthToBytes(ref result, ref index);

            TopicName.SrtingToMqttUTF8Bytes(ref result, ref index);

            if (QoS != QoSType.QoS0) PacketIdentifier.WriteBytes(ref result, ref index);

            Body.AsSpan().TryCopyTo(result[index..(index + Body.Length)]);
            return result;
        }
    }
}
